#pragma once

#include <algorithm>
#include <limits>

#include <boost/circular_buffer.hpp>
#include <deque>

#include "../utils/misc.h"

namespace mdtx::online::impl
{
    template <typename T, bool S>
    class ohlc;

    template <typename T>
    class ohlc<T, false>
    {
    public:
        T o, h, l, c;
        bool init;

        void insert_impl(T x)
        {
            if (init)
            {
                c = x;
                h = std::max<T>(h, x);
                l = std::min<T>(l, x);
            }
            else
            {
                o = h = l = c = x;
                init = true;
            }
        }

        void reset_impl()
        {
            init = false;
            o = c = 0;
            h = std::numeric_limits<T>::lowest();
            l = std::numeric_limits<T>::max();
        }
    };

    template <typename T>
    class ohlc<T, true>
    {
    public:
        boost::circular_buffer<T> buf;
        std::deque<T> min{}, max{};

        explicit ohlc(size_t nbuf0 = 16)
            : buf(nbuf0)
        {
        }

        void insert_impl(T x)
        {
            if (buf.full())
                buf.set_capacity(buf.size() * 2);
            buf.push_back(x);
            while (!min.empty() && min.back() > x)
                min.pop_back();
            while (!max.empty() && max.back() < x)
                max.pop_back();
            min.push_back(x);
            max.push_back(x);
        }

        void remove_impl(T)
        {
            T x = buf.front();
            buf.pop_front();
            if (!min.empty() && min.front() == x)
                min.pop_front();
            if (!max.empty() && max.front() == x)
                max.pop_front();
        }

        void reset_impl()
        {
            buf.clear();
            min.clear();
            max.clear();
        }
    };

    template <typename T>
    class ohlc4 : public ohlc<T, false>
    {
    public:
        void insert_impl(T o, T h, T l, T c)
        {
            if (this->init)
            {
                this->c = c;
                this->h = std::max<T>(this->h, h);
                this->l = std::min<T>(this->l, l);
            }
            else
            {
                this->o = o;
                this->h = h;
                this->l = l;
                this->c = c;
                this->init = true;
            }
        }
    };

    template <typename T>
    class vwap
    {
    public:
        T vol, tnvr;
        kahan<T> acc_vol, acc_tnvr;

        void insert_impl(T v, T t)
        {
            acc_vol(vol, v);
            acc_tnvr(tnvr, t);
        }

        void remove_impl(T v, T t)
        {
            acc_vol(vol, -v);
            acc_tnvr(tnvr, -t);
        }

        void reset_impl()
        {
            vol = tnvr = 0;
            acc_vol.reset();
            acc_tnvr.reset();
        }
    };
}
